package com.weic.easycache.access;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.apache.http.protocol.HTTP;

import android.util.Log;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * An {@link INetAces} based on {@link HttpURLConnection}.
 */
public class HttpService_v9u extends AbsNetAces {

	public HttpService_v9u(boolean jsonAndStatus, boolean gzipEnabled) {
		super(jsonAndStatus, gzipEnabled);
	}

	@Override
	protected HttpConnResp execAccess(String url, int method, Map<String, String> headers,
			List<NameValuePair> entityParams) throws UnknownHostException,
			ConnectTimeoutException, SocketTimeoutException, IOException {
		if(LOG) Log.i(LOG_TAG, "[HttpService_v9u.execAccess]");
		Map<String, String> headMap = new HashMap<String, String>();

		Map<String, String> defHeaders = getDefaultHeaders();
		if(defHeaders != null) headMap.putAll(defHeaders);
		if(headers != null) headMap.putAll(headers);
		//方法connection.addRequestProperty()不会覆盖，后面有设置，这里应该删除
		headMap.remove(HTTP.CONTENT_TYPE);
		HttpURLConnection connection = null;
		BasicHttpResponse response = null;
		try {
			connection = openConnection(new URL(url));
			for (String headerName : headMap.keySet()) {
				connection.addRequestProperty(headerName, headMap.get(headerName));
			}
			setConnectionParametersForRequest(connection, method, entityParams, getDefaultEncoding());
			int responseCode = connection.getResponseCode();
			if (responseCode == -1) {
				// -1 is returned by getResponseCode() if the response code could not be retrieved.
				// Signal to the caller that something was wrong with the connection.
				throw new IOException("Could not retrieve response code from HttpUrlConnection.");
			}
			// Initialize HttpResponse with data from the HttpURLConnection.
			ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
			StatusLine statusLine = new BasicStatusLine(protocolVersion,
					connection.getResponseCode(), connection.getResponseMessage());
			response = new BasicHttpResponse(statusLine);
			response.setEntity(entityFromConnection(connection));
			for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
				if (header.getKey() != null) {
					response.addHeader(new BasicHeader(header.getKey(), header.getValue().get(0)));
				}
			}
			/* 不要用finally，否则在输入流数据读取之前，连接已经关闭了，将读不到数据。
			 * 不过这种写法不好，应该在这里用回调的方式处理。
			 */
		} catch (UnknownHostException e) {
			if(connection != null) connection.disconnect();
			throw e;
		} catch (ConnectTimeoutException e) {
			if(connection != null) connection.disconnect();
			throw e;
		} catch (SocketTimeoutException e) {
			if(connection != null) connection.disconnect();
			throw e;
		} catch (IOException e) {
			if(connection != null) connection.disconnect();
			throw e;
		}
		return new HttpConnResp(response, connection);
	}

	/**
	 * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
	 * @param connection
	 * @return an HttpEntity populated with data from <code>connection</code>.
	 */
	private static HttpEntity entityFromConnection(HttpURLConnection connection) {
		BasicHttpEntity entity = new BasicHttpEntity();
		InputStream inputStream;
		try {
			inputStream = connection.getInputStream();
		} catch (IOException ioe) {
			inputStream = connection.getErrorStream();
		}
		entity.setContent(inputStream);
		entity.setContentLength(connection.getContentLength());
		entity.setContentEncoding(connection.getContentEncoding());
		entity.setContentType(connection.getContentType());
		return entity;
	}

	protected HttpURLConnection createConnection(URL url) throws IOException {
		return (HttpURLConnection) url.openConnection();
	}

	private HttpURLConnection openConnection(URL url) throws IOException {
		HttpURLConnection connection = createConnection(url);

		int timeoutMs = getTimeoutMs();
		connection.setConnectTimeout(timeoutMs);
		connection.setReadTimeout(timeoutMs);
		connection.setUseCaches(false);
		connection.setDoInput(true);
		// use caller-provided custom SslSocketFactory, if any, for HTTPS
		/*if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
            ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
        }*/
		return connection;
	}

	private static void setConnectionParametersForRequest(HttpURLConnection connection,
			int method, List<NameValuePair> entityParams, String encoding) throws IOException {
		switch (method) {
		case Method.DEPRECATED_GET_OR_POST:
			if (entityParams != null) {
				// Prepare output. There is no need to set Content-Length explicitly,
				// since this is handled by HttpURLConnection using the size of the prepared
				// output stream.
				connection.setDoOutput(true);
				connection.setRequestMethod("POST");
				addBodyIfExists(connection, entityParams, encoding);
			}
			break;
		case Method.GET:
			// Not necessary to set the request method because connection defaults to GET but
			// being explicit here.
			connection.setRequestMethod("GET");
			break;
		case Method.DELETE:
			connection.setRequestMethod("DELETE");
			break;
		case Method.POST:
			connection.setRequestMethod("POST");
			addBodyIfExists(connection, entityParams, encoding);
			break;
		case Method.PUT:
			connection.setRequestMethod("PUT");
			addBodyIfExists(connection, entityParams, encoding);
			break;
		default:
			throw new IllegalStateException("Unknown method type.");
		}
	}

	private static void addBodyIfExists(HttpURLConnection connection,
			List<NameValuePair> entityParams, String encoding) throws IOException {
		if(entityParams != null) {
			connection.setDoOutput(true);
			connection.addRequestProperty(HTTP.CONTENT_TYPE, getBodyContentType());
			DataOutputStream out = null;
			try {
				out = new DataOutputStream(connection.getOutputStream());
				new UrlEncodedFormEntity(entityParams, encoding).writeTo(out);
			}finally {
				if(out != null) try { out.close(); }catch(IOException e) {}
			}
		}
	}
}
